/*
* mainwin.cpp - This file is the part of the wp-notifier.(code name "luan luan")
* Copyright (C) 2010 Dean Lee <deanlee3@gmail.com>, http://www.deanlee.cn
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
* 
* Acknowledgements:
*   <> Many thanks to all of you, who have encouraged me to update my articles
*     and code, and who sent in bug reports and fixes.
* 
*/
#include <QLabel>
#include <QMenu>
#include <QProgressBar>
#include <QCloseEvent>
#include <QDesktopWidget>
#include <QFileInfo>
#include <QMovie>
#include <QSound>
#include <QStatusBar>
#include "mainwin.h"
#include "aboutdlg.h"
#include "common.h"
#include "commentsmodel.h"
#include "blogwizard.h"
#include "configdlg.h"
#include "popupwnd.h"
#include "autoupdate.h"
#include "./qxmlrpc/client/src/xmlrpcconv.h"


MainWindow* MainWindow::s_instance = NULL;

MainWindow::MainWindow(QWidget* parent) : QWidget(parent)
{
	s_instance = this;
	ui_.setupUi(this);

#ifdef _DEBUG 
#if defined(Q_WS_WIN)
	QFileInfo fi(QString("%1/../%2").arg(qApp->applicationDirPath()).arg("/skins/default/"));
	StyleWatchThread* t = new StyleWatchThread(fi.canonicalFilePath(), this);
	connect(t, SIGNAL(styleChanged()), SLOT(applyStyleSheet()), Qt::QueuedConnection);
	t->start();
#endif
#endif
	applyStyleSheet();

	// statusbar
	m_loadingMovie = new QMovie(this);
	m_loadingMovie->setFileName(":/icons/loading.gif");
	m_loadingMovie->setCacheMode(QMovie::CacheAll);
	ui_.lb_loading->setMovie(m_loadingMovie);

	// load configuration
	if (!loadOptions()) {
		QTimer::singleShot(0, this, SLOT(closeProgram()));
		return;
	}
	foreach(CommentsModel* m, m_models)
	{
		ui_.cb_blogs->addItem(m->blog().name(), m->blog().id());
		connect(m, SIGNAL(hasNewComments(int, int, int)), SLOT(hasNewComments(int, int, int)));
		connect(m, SIGNAL(commentsCountChanged()), SLOT(commentsCountChanged()));
		
#ifdef _DEBUG
		// test popup
		PopupWnd* wnd = new PopupWnd(m->blog());
		wnd->setMessage(tr("<html><body>you have got %1 new comments<br />"
			"%2 in <span style=\"color:#006505\">approved</span><br />"
			"%3 in <span style=\"color:#e6853a\">pending</span><br />"
			"%4 in <span style=\"color:#d30b3f\">spam</span></body></html>").arg(1 + 1 + 1)
			.arg(1).arg(1).arg(1));
		QSound::play(QApplication::applicationDirPath() + "/newcomments.wav");
		connect(wnd, SIGNAL(clicked(int)), SLOT(popupClicked()));
		wnd->show();
#endif
	}
	//ui_.f_blogs->setVisible(s_opt.m_blogs.count() > 1);

	connect(ui_.cb_blogs, SIGNAL(currentIndexChanged(int)), SLOT(currentIndexChanged(int)));
	refresh();

	m_filterModel = new CommentFilterModel(this);

	ui_.lv_comments->setModel(m_filterModel);
	m_delegate = new CommentItemDelegate(ui_.lv_comments);
	ui_.lv_comments->setItemDelegate(m_delegate);
	connect(ui_.lv_comments, SIGNAL(replyComment(const QModelIndex&)), SLOT(replyComment(const QModelIndex&)));

	m_refreshTimer = new QTimer(this);
	m_refreshTimer->setSingleShot(true);
	m_refreshTimer->setInterval(s_opt.m_checkInterval);
	connect(m_refreshTimer, SIGNAL(timeout()), this, SLOT(refresh()));

	m_trayIconFlashTimer = new QTimer(this);
	m_trayIconFlashTimer->setInterval(400);
	connect(m_trayIconFlashTimer, SIGNAL(timeout()), this, SLOT(flashTrayIcon()));
	

	buildActions();

	// tool buttons
	ui_.tb_hold->setChecked(true);
	connect(ui_.tb_approve, SIGNAL(toggled(bool)), SLOT(setChecked(bool)));
	connect(ui_.tb_hold, SIGNAL(toggled(bool)), SLOT(setChecked(bool)));
	connect(ui_.tb_spam, SIGNAL(toggled(bool)), SLOT(setChecked(bool)));
	ui_.tb_refresh->setDefaultAction(action("refresh"));
	ui_.tb_config->setDefaultAction(action("config"));
	ui_.te_comment->installEventFilter(this);
	ui_.tb_approve->setChecked(true);
	ui_.bottomFrame->setVisible(false);

		currentIndexChanged(0);

	
#if defined(Q_WS_WIN)
	if (s_opt.m_autoStart) {
		setStartup(true);
	}
#endif
	resize(s_opt.m_winSize);
	createTray();
	checkForUpdate();
}

MainWindow::~MainWindow()
{
}

void MainWindow::saveOptions()
{
	bool result = false;
	QFile file(homeDir() + "/config.xml");
	if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
		QDomDocument doc;
		QDomElement el = s_opt.toXml(&doc);
		QDomElement blogs = doc.createElement("blogs");
		foreach(CommentsModel* m, m_models) {
			blogs.appendChild(m->toXml(&doc));
		}
		el.appendChild(blogs);
		doc.appendChild(el);
		QTextStream text;
		text.setDevice(&file);
		text.setCodec("UTF-8");
		text << doc.toString();
		result = file.error() == QFile::NoError;
		file.close();
	}
}

bool MainWindow::loadOptions()
{
	bool result = false;
	QFile file(homeDir() + "/config.xml");
	if (file.open(QIODevice::ReadOnly)) {
		QDomDocument doc;
		result = doc.setContent(&file);
		file.close();
		if (result) {
			bool found = false;
			s_opt.fromXml(doc.documentElement());
			QDomElement blogs = findSubTag(doc.documentElement(), "blogs", &found);
			if (found) {
				for(QDomNode n = blogs.firstChild(); !n.isNull(); n = n.nextSibling()) {
					QDomElement i = n.toElement();
					if(i.isNull())
						continue;

					if(i.tagName() == "blog") {
						CommentsModel* model = new CommentsModel();
						model->fromXml(i);
						m_models << model;
					}
				}
			}
		}
	}
	if (!result || m_models.isEmpty())
	{
		BlogWizard wizard(NULL);
		wizard.setModal(true);
		if (QDialog::Accepted == wizard.exec()) {
			foreach(Blog blog, wizard.blogs()) {
				CommentsModel* model = new CommentsModel(this);
				m_models << model;
			}
			saveOptions();
		}
		else
		{
			return false;
		}
	}
	/*static QString files[] = {"approve.xml", "hold.xml", "spam.xml"};
	QList<Comment*> comments;
	for (int i = 0; i < _countof(files); ++i) {
		QFile file(homeDir() + "/" + files[i]);
		if (file.open(QIODevice::ReadOnly)) {
			QString err;
			QVariant response  = fromXmlRpcResponse( file.readAll(), err );
			comments += Comment::responseToCommentList(response);
			file.close();
		}
	}
	m_model->setComments(comments);*/
	return true;
}

void MainWindow::showEvent(QShowEvent * event)
{
	QWidget::showEvent(event);
}

void MainWindow::doConfig()
{
	ConfigDlg* dlg = ConfigDlg::showConfigDlg();
	connect(dlg, SIGNAL(accountDeleted(Blog*)), SLOT(accountDeleted(Blog*)));
	connect(dlg, SIGNAL(accountAdded(Blog*)), SLOT(accountChanged(Blog*)));
	connect(dlg, SIGNAL(accountUpdated(Blog*)), SLOT(accountChanged(Blog*)));

}

void MainWindow::setChecked(bool bChecked)
{
	static QToolButton* buttons[] = {ui_.tb_approve, ui_.tb_hold, ui_.tb_spam};
	QToolButton* btn = static_cast<QToolButton*>(sender());
	if (bChecked) {
		if (btn == ui_.tb_approve) {
			m_filterModel->setFilter(CommentData::approve);
		}
		else if (btn == ui_.tb_hold) {
			m_filterModel->setFilter(CommentData::hold);
		}
		else if (btn == ui_.tb_spam) {
			m_filterModel->setFilter(CommentData::spam);
		}
	}
}

void MainWindow::hasNewComments(int approve, int hold, int spam)
{
	/*if (s_opt.m_bRaiseManWindow) {
		bringToFront(this);
	}*/
	CommentsModel* model = static_cast<CommentsModel*>(sender());
	PopupWnd* wnd = new PopupWnd(model->blog());
	wnd->setMessage(tr("<html><body>you have got %1 new comments<br />"
		"%2 in <span style=\"color:#006505\">approved</span><br />"
		"%3 in <span style=\"color:#e6853a\">pending</span><br />"
		"%4 in <span style=\"color:#d30b3f\">spam</span></body></html>").arg(approve + hold + spam)
		.arg(approve).arg(hold).arg(spam));
	QSound::play(QApplication::applicationDirPath() + "/newcomments.wav");
	connect(wnd, SIGNAL(clicked(int)), SLOT(raiseMainwnd()));
	wnd->show();
	m_trayIconFlashTimer->start();
}

void MainWindow::actAbout()
{
	AboutDlg::showDlg();
}

void MainWindow::createTray()
{
	m_trayIcon1 = new QIcon(":/icons/comment.png");
	m_trayIcon2 = new QIcon(":/icons/comment2.png");
	m_tray = new QSystemTrayIcon(this);
	m_tray->setIcon(*m_trayIcon1);
	QMenu *menu = new QMenu(this);
	menu->addAction(action("config"));
	menu->addAction(action("refresh"));
	menu->addSeparator();
	menu->addAction(action("update"));
	menu->addAction(action("about"));
	menu->addSeparator();
	menu->addAction(action("exit"));
	m_tray->setContextMenu(menu);
	m_tray->show();

	connect(m_tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
	connect(m_tray, SIGNAL(messageClicked()), SLOT(show()));
}

void MainWindow::closeProgram()
{
//	s_opt.save();
	wpCommand::shutDownAllCommands();
	qApp->quit();
}

void MainWindow::closeEvent(QCloseEvent *e)
{
	hide();
	e->ignore();
}

void MainWindow::resizeEvent(QResizeEvent *e)
{
	s_opt.m_winSize = e->size();
	QWidget::resizeEvent(e);
}


void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason)
{
	if (reason == QSystemTrayIcon::Trigger) {
		raiseMainwnd();
	}
}

void MainWindow::replyComment(const QModelIndex& index)
{
	m_replyIndex = index;
	if (!m_replyIndex.isValid())
		return;
	CommentsModel::CommentItem* item = (CommentsModel::CommentItem*)index.internalPointer();
	ui_.bottomFrame->setVisible(true);
	ui_.te_comment->setText("@" + item->comment.author() + ":");
	ui_.te_comment->moveCursor(QTextCursor::End);
	ui_.te_comment->setFocus();
}

void MainWindow::doPostComment()
{
	QString comment = ui_.te_comment->toPlainText().trimmed();
	if (!comment.isEmpty()) {
		ui_.te_comment->setText(QString());
		ui_.bottomFrame->setVisible(false);
		//m_currentBlog->m_model->replyComment(m_replyIndex, comment);
	}
}

void MainWindow::busy(const QString& message)
{
	ui_.lb_message->setText(message);
	ui_.lb_loading->setVisible(true);
	m_loadingMovie->start();
}

void MainWindow::noBusy()
{
	ui_.lb_message->setText(tr("Ready"));
	ui_.lb_loading->setVisible(false);
	m_loadingMovie->stop();
}

void MainWindow::positionToRightBottom()
{
	QRect geom = qApp->desktop()->availableGeometry(this);
	QPoint destination(geom.x() + geom.width(), geom.y() + geom.height()); 

	QRect rcMe = frameGeometry();
	int width = rcMe.width();
	int height = rcMe.height();
	if ( (destination.x() + width) > (geom.x() + geom.width()) )
		destination.setX( geom.x() + geom.width() - width );

	if ( destination.x() < 0 )
		destination.setX( 0 );

	if ( (destination.y() + height) > (geom.y() + geom.height()) )
		destination.setY( geom.y() + geom.height() - height );

	if ( destination.y() < 0 )
		destination.setY( 0 );
	move(destination);
}

void MainWindow::commentsCountChanged()
{
	CommentsModel* m = m_models.at(ui_.cb_blogs->currentIndex());

	//if (sender() != NULL && sender() != this) {
	//	/*CommentsModel* model = static_cast<CommentsModel*>(sender());
	//	Blog* b = (Blog*)model->blog();
	//	b->m_nApprove = approved;
	//	b->m_nHold = hold;
	//	b->m_nSpam = spam;*/
	//}
	ui_.tb_approve->setText(QString("%1").arg(m->approved()));
	ui_.tb_hold->setText(QString("%1").arg(m->hold()));
	ui_.tb_spam->setText(QString("%1").arg(m->spam()));

}

void MainWindow::applyStyleSheet()
{
	QString templateFile;
#ifdef _DEBUG
	QFileInfo fi(QString("%1/../skins/default/style.qss").arg(QApplication::applicationDirPath()));
	templateFile = fi.canonicalFilePath();
#else
	templateFile = ":/skins/default/style.qss";
#endif
	QString strStyle;
	QFile file(templateFile);
	if (file.open(QFile::ReadOnly)) {
		strStyle = QLatin1String(file.readAll());
		//setStyleSheet(strStyle);
		qApp->setStyleSheet(strStyle);
	}
}

bool MainWindow::eventFilter(QObject* obj, QEvent* event)
{
	if (obj == ui_.te_comment && event->type() == QEvent::KeyPress) {
		QKeyEvent* e = static_cast<QKeyEvent*>(event);
		if ((e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) && !(e->modifiers() & Qt::ShiftModifier)) {
			doPostComment();
			event->accept();
			return true;
		}
		else if (e->key() == Qt::Key_Escape) {
			ui_.bottomFrame->setVisible(false);
			event->accept();
			return true;
		}
	}
	return QWidget::eventFilter(obj, event);
}

QAction* MainWindow::action(const QString& name)
{
	QAction* action = m_actionMap.value(name);
	Q_ASSERT(action);
	return action;
}

void MainWindow::buildActions()
{
	struct ActionStruct
	{
		QString name;
		QString icon;
		QString text;
		QObject *receiver;
		const char* slot;
	};
	ActionStruct actions[] = {
		{"exit", ":/icons/quit.png", tr("&Exit"), this, SLOT(closeProgram())},
		{"about", ":/icons/about.png", tr("&about"), this, SLOT(actAbout())},
		{"config", ":/icons/config.png", tr("&Configuration"), this, SLOT(doConfig())},
		{"refresh", ":/icons/refresh.png", tr("&refresh"), this, SLOT(refresh())},
		{"delete", ":/icons/delete.png", tr("&delete"), ui_.lv_comments, SLOT(actDelete())},
		{"spam", ":/icons/spam.png", tr("&spam"), ui_.lv_comments, SLOT(actSpam())},
		{"approve", ":/icons/approve.png", tr("&approve"), ui_.lv_comments, SLOT(actApprove())},
		{"edit", ":/icons/edit.png", tr("&edit"), ui_.lv_comments, SLOT(actEdit())},
		{"unapprove", ":/icons/hold.png", tr("&unapprove"), ui_.lv_comments, SLOT(actHold())},
		{"hold", ":/icons/hold.png", tr("&hold"), ui_.lv_comments, SLOT(actHold())},
		{"notspam", ":/icons/hold.png", tr("&not spam"), ui_.lv_comments, SLOT(actHold())},
		{"reply", ":/icons/reply.png", tr("&reply"), ui_.lv_comments, SLOT(actReply())},
		{"update", ":/icons/globe.png", tr("&Check f&or updates"), this, SLOT(checkForUpdate())},
	};
	for (int i = 0; i < _countof(actions); ++i) {
		const ActionStruct& act = actions[i];
		QAction *action = new QAction(QIcon(act.icon), act.text, this);
		m_actionMap.insert(act.name, action);
		connect(action, SIGNAL(triggered(bool)), act.receiver, act.slot);
	}
}

void MainWindow::contextMenuEvent(QContextMenuEvent *)
{
	m_tray->contextMenu()->exec(QCursor::pos());
}

void MainWindow::popupClicked()
{
	PopupWnd* wnd = static_cast<PopupWnd*>(sender());
	Blog b = wnd->blog();
	for (int i = 0; i < m_models.count(); ++i){
		CommentsModel* m = m_models.at(i);
		if (m->blog().id() == b.id() && m->blog().xmlrpcUrl() == b.xmlrpcUrl()) {
			//currentIndexChanged(i);
			ui_.cb_blogs->setCurrentIndex(i);
			raiseMainwnd();
			break;
		}
	}
}

void MainWindow::raiseMainwnd()
{
	bringToFront(this);
	m_trayIconFlashTimer->stop();
	m_tray->setIcon(*m_trayIcon1);
}

void MainWindow::flashTrayIcon()
{
	static bool i = false;
	i = !i;
	m_tray->setIcon(i ? *m_trayIcon2 : *m_trayIcon1);
}


void MainWindow::updateAvailable(const QString& /*newVersion*/)
{
	NewVersionDlg *dlg = new NewVersionDlg(tr("a new version %1 is available <br /> "
		"Click <a href=\"%2\">here</a> to download the lastest version.").arg("").arg(DOWNLOAD_URL));
	dlg->show();
}

void MainWindow::checkForUpdate()
{
	AutoUpdate* update = new AutoUpdate(this);
	connect(update, SIGNAL(updateAvailable(const QString&)), SLOT(updateAvailable(const QString&)));

}

void MainWindow::refresh()
{
	if (sender() == m_refreshTimer || !sender()) {
		foreach(CommentsModel* m, m_models) {
			m->refresh();
		}
	}
	else {
		m_models.at(ui_.cb_blogs->currentIndex())->refresh();
		//m_currentBlog->m_model->refresh();
	}
}

void MainWindow::currentIndexChanged(int index)
{
	/*Blog* blog = s_opt.m_blogs.at(index);
	m_currentBlog = blog;*/
	CommentsModel* m = m_models.at(index);
	setWindowTitle(m->blog().name());
	m_filterModel->setSourceModel(m);
	commentsCountChanged();
}

void MainWindow::accountChanged(Blog* blog)
{
	/*bool found = false;
	foreach(Blog* oldBlog, s_opt.m_blogs) {
		if (oldBlog == blog) {
			oldBlog->m_model->setComments(QList<Comment>());
			oldBlog->m_model->refresh();
			found = true;
			break;
		}
	}

	if (!found) {
		ui_.cb_blogs->addItem(blog->m_blog_name, blog->m_blog_id);
		connect(blog->m_model, SIGNAL(hasNewComments(int, int, int)), SLOT(hasNewComments(int, int, int)));
		connect(blog->m_model, SIGNAL(commentsCountChanged(const Blog*)), SLOT(commentsCountChanged(const Blog*)));
		blog->m_model->refresh();
		if (ui_.cb_blogs->count() > 1) {
			ui_.f_blogs->setVisible(true);
		}
	}*/
}



void MainWindow::accountDeleted(Blog* blog)
{
	/*for (int i = 0; i < s_opt.m_blogs.count(); ++i) {
		if (s_opt.m_blogs.at(i) == blog) {
			ui_.cb_blogs->removeItem(i);
			break;
		}
	}
	if (m_currentBlog == blog) {
		for (int i = 0; i < s_opt.m_blogs.count(); ++i) {
			if (s_opt.m_blogs.at(i) != blog) {
				this->currentIndexChanged(i);
				break;
			}
		}
	}
	if (ui_.cb_blogs->count() == 1) {
		ui_.f_blogs->setVisible(false);
	}*/
}

